﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Extensions.DependencyModel;
using HamcoWcs.Entitys.Bases;
using HamcoWcs.Entitys.DbInfos;
using HamcoWcs.Entitys.UserInfos;

//using HamcoWcs.Entitys.UserInfos;

namespace HamcoWcs.DAL
{
    public partial class WcsDbContext
    {
        private string AddUndercoresToSentence(string text, bool preserveAcronyms)
        {
            if (string.IsNullOrWhiteSpace(text))
                return string.Empty;
            var newText = new StringBuilder(text.Length * 2);
            newText.Append(char.ToLower(text[0]));
            for (int i = 1; i < text.Length; i++)
            {
                if (char.IsUpper(text[i]))
                    if ((text[i - 1] != '_' && !char.IsUpper(text[i - 1])) ||
                        (preserveAcronyms && char.IsUpper(text[i - 1]) &&
                         i < text.Length - 1 && !char.IsUpper(text[i + 1])))
                        newText.Append('_');
                newText.Append(char.ToLower(text[i]));
            }
            return newText.ToString();
        }
        public void SetGlobalQuery<T>(ModelBuilder builder) where T : BaseEntity
        {
            builder.Entity<T>().ToTable(AddUndercoresToSentence(typeof(T).Name, false));
            builder.Entity<T>().Property(x => x.Id).HasComment("主键Id");
            builder.Entity<T>().HasKey(e => e.Id);
            foreach (var property in builder.Model.GetEntityTypes()
                .SelectMany(t => t.GetProperties())
                .Where(p => p.ClrType == typeof(string)))
            {
                property.AsProperty().Builder
                        .HasMaxLength(100, ConfigurationSource.Convention);
            }
        }

        static readonly MethodInfo SetGlobalQueryMethod = typeof(WcsDbContext).GetMethods(BindingFlags.Public | BindingFlags.Instance)
            .Single(t => t.IsGenericMethod && t.Name == "SetGlobalQuery");
        private static IList<Type> _entityTypeCache;


        private static IList<Type> GetEntityTypes()
        {
            if (_entityTypeCache != null)
            {
                return _entityTypeCache.ToList();
            }

            _entityTypeCache = (from a in GetReferencingAssemblies()
                                from t in a.DefinedTypes
                                where t.IsSubclassOf(typeof(BaseEntity)) && t != typeof(AuditEntity)
                                select t.AsType()).ToList();

            return _entityTypeCache;
        }

        private static IEnumerable<Assembly> GetReferencingAssemblies()
        {
            var assemblies = new List<Assembly>();
            var dependencies = DependencyContext.Default.RuntimeLibraries;

            foreach (var library in dependencies)
            {
                try
                {
                    var assembly = Assembly.Load(new AssemblyName(library.Name));
                    assemblies.Add(assembly);
                }
                catch (FileNotFoundException)
                { }
            }
            return assemblies;
        }

        private void OnBeforeSaving()
        {
            var entries = ChangeTracker.Entries();
            foreach (var entry in entries)
            {
                if (entry.Entity is AuditEntity trackable)
                {
                    var now = DateTime.Now;
                    switch (entry.State)
                    {
                        case EntityState.Modified:
                            trackable.LastUpdateTime = now;
                            break;

                        case EntityState.Added:
                            //备份时不修改CreateTime
                            if (trackable.CreatedTime == default(DateTime))
                            {
                                trackable.CreatedTime = now;
                            }
                            trackable.LastUpdateTime = now;
                            break;
                            // case EntityState.Deleted:
                            //     entry.State = EntityState.Modified;
                            //     trackable.ModifiedById = userId;
                            //     trackable.LastModified = now;
                            //     trackable.IsDeleted = true;
                            //     break;
                    }
                }

            }
        }

        public override int SaveChanges()
        {
            OnBeforeSaving();
            return base.SaveChanges();
        }
        public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            OnBeforeSaving();
            return base.SaveChangesAsync(cancellationToken);
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            // optionsBuilder
            //     .UseSnakeCaseNamingConvention();
            // var connectionString = "Server=localhost;port=3306;database=xxal_wcs;User=root;pwd=Abc4052816;";
            //
            // // Replace with your server version and type.
            // // Use 'MariaDbServerVersion' for MariaDB.
            // // Alternatively, use 'ServerVersion.AutoDetect(connectionString)'.
            // // For common usages, see pull request #1233.
            // var serverVersion = new MySqlServerVersion(new Version(5, 7, 21));
            //
            // // Replace 'YourDbContext' with the name of your own DbContext derived class.
            // optionsBuilder.UseMySql(connectionString, serverVersion);
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            foreach (var type in GetEntityTypes())
            {
                var method = SetGlobalQueryMethod.MakeGenericMethod(type);
                method.Invoke(this, new object[] { modelBuilder });
            }

            modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetAssembly(typeof(DbInfoConfig)));
            base.OnModelCreating(modelBuilder);
            //modelBuilder.Entity<UserInfo>().HasData(new UserInfo()
            //{
            //    UserName="管理员",
            //    Password= "21232F297A57A5A743894A0E4A801FC3",
            //    UserNo="admin",
            //    UserRole=1,
            //    Enabled=true,
            //});
        }
    }
}